package xmlsync2;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.text.MessageFormat;
import java.util.LinkedList;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;

import datawave.util.IOUtil;
import datawave.util.LoggerCache;
/**
 * @author jinwoo
 *
 * To change this generated comment edit the template variable "typecomment":
 * Window>Preferences>Java>Templates.
 * To enable and disable the creation of type comments go to
 * Window>Preferences>Java>Code Generation.
 */
public class RecordSet
{
	LinkedList definition;
	LinkedList records; // LinkedList of LinkedList
	String encoding = null;
	private static Logger log = LoggerCache.get(RecordSet.class.getName());

 	/**
	 * Constructor RecordSet.
	 * @param resultset
	 */

	public RecordSet(final ResultSet resultset, final String encoding) throws SQLException, XmlsyncException
	{
		this.encoding = encoding;
		records = new LinkedList();
		definition = new LinkedList();
		ResultSetMetaData resultsetmetadata= resultset.getMetaData();
		log.debug("the number of RecordSet columns : "+resultsetmetadata.getColumnCount());
		for (int i=1; i<=resultsetmetadata.getColumnCount(); i++)
		{
			definition.addLast(new Field(resultsetmetadata.getColumnName(i), resultsetmetadata.getColumnType(i), resultsetmetadata.getColumnTypeName(i)));
		}
		while (resultset.next())
			add(resultset);
		resultset.close();
	}
	
	/**
	 * Method add.
	 * @param resultset
	 */
	private void add(ResultSet resultset) throws SQLException, XmlsyncException
	{
		LinkedList record = new LinkedList();
		for (int i=0; i<definition.size(); i++)
		{
			record.addLast(getvalue(resultset, i));
		}
		records.addLast(record);
	}

	/**
	 * Method getvalue.
	 * @param resultset
	 * @param i
	 * @return Object
	 * Types.BIGINT        -5   
	 * Types.BINARY        -2   
	 * Types.BIT           -7   
	 * Types.BLOB          2004 
	 * Types.CHAR          1    
	 * Types.CLOB          2005 
	 * Types.DATE          91   
 	 * Types.DECIMAL       3    
	 * Types.DISTINCT      2001 
	 * Types.DOUBLE        8    
	 * Types.FLOAT         6    
	 * Types.INTEGER       4    
	 * Types.JAVA_OBJECT   2000 
	 * Types.LONGVARBINARY -4   
	 * Types.LONGVARCHAR   -1   
	 * Types.NULL          0    
	 * Types.NUMERIC       2    
	 * Types.OTHER         1111 
	 * Types.REAL          7    
	 * Types.REF           2006 
	 * Types.SMALLINT      5    
	 * Types.STRUCT        2002 
	 * Types.TIME          92   
	 * Types.TIMESTAMP     93   
	 * Types.TINYINT       -6   
	 * Types.VARBINARY     -3   
	 * Types.VARCHAR       12   
	 *  
	 */
	private String getvalue(final ResultSet resultset, final int index) throws SQLException, XmlsyncException
	{
		switch (((Field)definition.get(index)).gettype())
		{
			case Types.BIT:
				if (resultset.getBoolean(index+1)) 
					return "true";
				else return "false";
			case Types.DATE :
			case Types.TIME :
			case Types.TIMESTAMP :
				return Str.dateformat(resultset.getDate(index+1));
			case Types.OTHER :
				return oracletype2str(resultset, index+1, ((Field)definition.get(index)).typename);
			case Types.CHAR :
			case Types.LONGVARCHAR :
			case Types.VARCHAR :
				if (this.encoding == null) return resultset.getString(index+1);
				else try
				{
					String v = resultset.getString(index+1);
					if ( v == null) return null;
					else return new String(v.getBytes(this.encoding), "euc-kr");
				}
				catch (UnsupportedEncodingException e)    
				{
					Object[] args = {this.encoding};
					String msg = MessageFormat.format(Messages.getString("XMLSYNC0007"), args);
					log.error(msg);
					throw new XmlsyncException(msg);
				}
				catch (SQLException e)
				{
					log.error("message : "+e.getMessage());
				}
				catch (Exception e)
				{
					Object[] args = {e.getMessage()};
					String msg = MessageFormat.format(Messages.getString("XMLSYNC0002"), args);
					log.error(msg);
					e.printStackTrace();
					throw new XmlsyncException(msg);
				}
			case Types.NUMERIC :
				return resultset.getString(index+1);
			case Types.BLOB :
				return blobasfile(resultset.getBlob(index+1));
			case Types.CLOB :
				return clobasfile(resultset.getClob(index+1));
			default :
				log.info("default conversion occurred. data type : "+((Field)definition.get(index)).gettype());
				return resultset.getString(index+1); 
		}
	}

	/**
	 * Method clobasfile.
	 * @param clob
	 * @return String
	 */
	private String clobasfile(Clob clob)
	{
		if (clob == null) return null;
		BufferedInputStream bis=null;
		byte[] buf=null;
		String filepath = "";
		try
		{
			bis = new BufferedInputStream(clob.getAsciiStream());
			buf = new byte[(int) clob.length()];
			bis.read(buf);
			bis.close();
			filepath = IOUtil.uniquefile();
			IOUtil.write2File(filepath, buf);
		}
		catch (IOException e)
		{
			log.error(e.getMessage());
		}
		catch (SQLException e)
		{
			log.error(e.getMessage());
		}
		return filepath;
	}


	/**
	 * Method blobasfile.
	 * @param blob
	 * @return String
	 */
	private String blobasfile(Blob blob)
	{
		if (blob == null) return null;
		BufferedInputStream bis=null;
		byte[] buf=null;
		String filepath = "";
		try
		{
			bis = new BufferedInputStream(blob.getBinaryStream());
			buf = new byte[(int) blob.length()];
			bis.read(buf);
			bis.close();
			filepath = IOUtil.uniquefile();
			IOUtil.write2File(filepath, buf);
		}
		catch (IOException e)
		{
			log.error(e.getMessage());
		}
		catch (SQLException e)
		{
			log.error(e.getMessage());
		}
		return filepath;
	}


	/**
	 * Method oracletype2str.
	 * @param resultset
	 * @param index
	 * @param string
	 * @return String
	 */
	private String oracletype2str(ResultSet resultset, final int i, final String typename) throws XmlsyncException
	{
		Object[] args = {typename};
		String msg = MessageFormat.format(Messages.getString("XMLSYNC0005"), args);
		log.error(msg);
		throw new XmlsyncException(msg);
	}

	/**
	 * Method documentfragment.
	 * @param doc
	 * @param name
	 * @return DocumentFragment
	 */
	public DocumentFragment documentfragment(Document doc, final String name)
	{
		DocumentFragment fragment = doc.createDocumentFragment();
		for (int i=0; i<records.size(); i++)
		{
			Element element = doc.createElement(name);
			setattributes(element, (LinkedList)records.get(i));
			fragment.appendChild(element);
		}
		return fragment;
	}

	/**
	 * Method setattributes.
	 * @param element
	 * @param linkedList
	 */
	private void setattributes(Element element, final LinkedList record)
	{
		for (int i=0; i<definition.size(); i++)
		if (record.get(i) != null)
			element.setAttribute(((Field)definition.get(i)).getname(), (String)record.get(i));
	}
}
